feat: add express.static dotfiles codemod#142
Conversation
Add a codemod that explicitly sets dotfiles: 'allow' on express.static() calls to preserve Express 4 behavior where dotfiles were served by default. In Express 5, the dotfiles option defaults to 'ignore', which can break functionality that depends on serving dot-directories like .well-known (used by Android App Links and Apple Universal Links). Closes expressjs#116
AugustinMauroy
left a comment
There was a problem hiding this comment.
good starting point
There was a problem hiding this comment.
need to bump minor version of this codemod 😁
There was a problem hiding this comment.
I bumped the codemod package version to 1.1.0 in the follow-up commit.
|
|
||
| This codemod adds an explicit `dotfiles: 'allow'` option to `express.static()` calls that don't already specify a `dotfiles` option, preserving the Express 4 behavior. | ||
|
|
||
| ## Example |
There was a problem hiding this comment.
IMO diff codeblock is easier to get what happened
| { pattern: 'express.static($PATH)' }, | ||
| { pattern: 'express.static($PATH, $OPTS)' }, |
There was a problem hiding this comment.
this ins't resolving import alias ...
before doing creazy thing wait @bjohansebas response.
Do you have any function to resolve express import in different cases
There was a problem hiding this comment.
I don't think there's any existing function to resolve that. I think the Node Userland Migration project has something we could take inspiration from, right?
But we should identify Express imports, because it's not always express.static; people can import it under different names.
There was a problem hiding this comment.
I added binding resolution for default, namespace, and CommonJS express imports before rewriting static() calls, and the multiline fixture is covered in tests.
|
|
||
| if (!nodes.length) return null | ||
|
|
||
| const edits: Edit[] = [] |
There was a problem hiding this comment.
| const edits: Edit[] = [] |
| import type { Edit, SgRoot } from '@codemod.com/jssg-types/src/main' | ||
|
|
||
| async function transform(root: SgRoot<Js>): Promise<string | null> { | ||
| const rootNode = root.root() |
There was a problem hiding this comment.
| const rootNode = root.root() | |
| const rootNode = root.root() | |
| const edits: Edit[] = [] |
| if (optsText.includes('dotfiles')) { | ||
| continue | ||
| } | ||
|
|
||
| if (optsText.startsWith('{') && optsText.endsWith('}')) { | ||
| const inner = optsText.slice(1, -1).trim() | ||
| const newOpts = inner | ||
| ? `{ ${inner}, dotfiles: 'allow' /* Express 5: preserve v4 behavior */ }` | ||
| : `{ dotfiles: 'allow' /* Express 5: preserve v4 behavior */ }` | ||
| edits.push(call.replace(`express.static(${pathArg.text()}, ${newOpts})`)) | ||
| } |
There was a problem hiding this comment.
this seem weak. add more test case with strange indentation + multi line
Detect default, namespace, and CommonJS express bindings before rewriting static() calls. Add a multiline fixture to cover indentation-sensitive options objects.
Match the reviewer request to bump the codemod minor version alongside the alias-handling fix.
|
Pushed 559f3d9. I now resolve default, namespace, and CommonJS bindings for express before rewriting static() calls, added a multiline fixture to cover indentation-sensitive options, and bumped the codemod version to 1.1.0. |
| const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)(?:\s*,[\s\S]*?)?\s+from\s+['"]express['"]/g | ||
| const namespaceImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+['"]express['"]/g | ||
| const defaultAsImportPattern = /import\s+\{\s*default\s+as\s+([A-Za-z_$][\w$]*)[\s\S]*?\}\s+from\s+['"]express['"]/g | ||
| const requirePattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*require\s*\(\s*['"]express['"]\s*\)/g |
There was a problem hiding this comment.
Pushed a1a6b47. I replaced the alias scan with AST/definition-based resolution, removed the regex path, and added a regression for require(express).static(...) so the commonjs case stays covered.
There was a problem hiding this comment.
Pushed a1a6b47. The alias lookup now uses AST/definition resolution only, the regex path is gone, and the commonjs case has a direct regression test.
In Express 5, the
express.staticmiddleware'sdotfilesoption defaults to"ignore"instead of serving them. This breaks functionality that depends on serving dot-directories like.well-known(used for Android App Links, Apple Universal Links, Let's Encrypt verification, etc.).This PR adds a new codemod that explicitly sets
dotfiles: 'allow'onexpress.static()calls that don't already specify adotfilesoption. A comment is included to flag the change for manual review.The codemod:
express.static('public')toexpress.static('public', { dotfiles: 'allow' /* Express 5: preserve v4 behavior */ })dotfilessettingThe README includes a security note encouraging developers to review each call and consider whether serving dotfiles is actually necessary.
Also added the new codemod to the v5-migration-recipe workflow.
Closes #116